package com.sos.flex.data
{
	import com.sos.Application;
	import com.sos.core.DataObject;
	import com.sos.core.DispatcherDataObject;
	import com.sos.core.Position;
	import com.sos.core.Role;
	import com.sos.core.School;
	import com.sos.core.cards.ActionCard;
	import com.sos.core.cards.Card;
	import com.sos.core.cards.GoalkeeperCard;
	import com.sos.core.cards.PlayerCard;
	import com.sos.data.filters.DataFilter;
	import com.sos.data.filters.DataFilters;
	import com.sos.data.filters.SchoolsFilter;
	import com.sos.data.filters.StringFilter;
	import com.sos.flex.ui.windows.ErrorWindow;
	import com.sos.logging.Dumper;
	import com.sos.logging.IDumpable;
	
	import flash.events.Event;
	import flash.utils.Dictionary;
	
	import mx.collections.ArrayCollection;
	import mx.collections.ArrayList;
	

	public class FlexDeck extends DispatcherDataObject implements IDumpable
	{
		
		// -------------------------------------------------------------------------------
		// CONSTANTS
		// -------------------------------------------------------------------------------
		
		
		public static const		DEFAULT_NAME:String 						= "new_deck";
		
		public static const		REQUIRED_ACTIONS_OLD:int					= 40;
		public static const		REQUIRED_ACTION_CARDS:int					= 50;
		
		// JSON Property names
		public static const 	JSON_PROPERTY_DECK:String					= "deck";
		public static const 	JSON_PROPERTY_FORMATION:String				= "formation";
		public static const 	JSON_PROPERTY_ACTION_CARDS:String			= "actionCards";
		public static const 	JSON_PROPERTY_DECK_ID:String				= "deckID";
		
		// Events
		public static const		EVENT_FORMATION_CHANGED:String 				= "formation_changed";
		public static const		EVENT_ACTION_LIST_CHANGED:String 			= "action_list_changed";
		public static const		EVENT_LEFT_ACTION_LIST_CHANGED:String 		= "left_action_list_changed";
		public static const		EVENT_LEFT_PLAYER_LIST_CHANGED:String 		= "left_player_list_changed";
		
		// -------------------------------------------------------------------------------
		// FIELDS
		// -------------------------------------------------------------------------------
		
		private var _id:uint;
		private var _name:String;
		private var _formation:Dictionary;
		private var _actionCards:ArrayCollection;
		private var _leftActionCards:ArrayCollection;
		private var _leftPlayerCards:ArrayCollection;
		private var _collection:FlexCollection;
		private var _actionCardsFilter:DataFilters;
		private var _leftActionCardsFilters:DataFilters;
		private var _leftPlayerCardsFilters:DataFilters;
		
		// -------------------------------------------------------------------------------
		// CONSTRUCTOR
		// -------------------------------------------------------------------------------		
		
		/**
		 * Create a new deck instance linked to the given card Collection
		 * @param collection
		 */
		public function FlexDeck(id:uint, name:String, collection:FlexCollection)
		{
			// Collection reference check
			if (collection == null) throw new Error("Collection should not be null!");
			// Initialization
			_name = name;
			_id = id;
			_collection = collection;
			// Create card list
			_actionCards = new ArrayCollection();
			_leftActionCards = new ArrayCollection();
			_leftPlayerCards = new ArrayCollection();
			// Creates the formation
			_formation = new Dictionary(true);
			_formation[Position.GOALKEEPER] 	= null;
			_formation[Position.DEFENDER_01] 	= null;
			_formation[Position.DEFENDER_02] 	= null;
			_formation[Position.MIDFIELD] 		= null;
			_formation[Position.FORWARD_01] 	= null;
			_formation[Position.FORWARD_02]		= null;
			_formation[Position.FORWARD_02]		= null;
			_formation[Position.SUB_DEFENCE]	= null;
			_formation[Position.SUB_MIDFIELD]	= null;
			_formation[Position.SUB_FORWARD]	= null;
			// Init filters
			_actionCardsFilter = new DataFilters();
			_leftActionCardsFilters = new DataFilters();
			_leftPlayerCardsFilters = new DataFilters();
		}
		
		// -------------------------------------------------------------------------------
		// PROPERTIES
		// -------------------------------------------------------------------------------
		
		public function get collection():FlexCollection
		{
			return _collection;
		}

		public function get id():uint
		{
			return _id;
		}
		
		public function set id(value:uint):void
		{
			_id = value;
		}
		
		public function get name():String
		{
			return _name;
		}
		
		public function set name(value:String):void
		{
			_name = value;
		}

		[Bindable(event="formation_changed")]
		public function get formation():Dictionary
		{
			return _formation;
		}

		[Bindable(event="action_list_changed")]
		public function get actionCards():ArrayCollection
		{
			return _actionCards;
		}
		
		[Bindable(event="action_list_changed")]
		public function get actionCardsCount():int
		{
			return _actionCards.source.length;
		}
		
		[Bindable(event="left_action_list_changed")]
		public function get leftActionCards():ArrayCollection
		{
			return _leftActionCards;
		}
		
		[Bindable(event="left_action_list_changed")]
		public function get leftActionCardsCount():int
		{
			return _leftActionCards.source.length;
		}
		
		[Bindable(event="action_list_changed")]
		public function get missingActionsCount():int
		{
			// 2012-07-23 Old version with fixed limit 
			// return Deck.REQUIRED_ACTIONS - _actionCards.length;
			
			// 2012-07-23 if present in the logged User instance, uses the valued provided
			// by at the login phase
			return this.requiredActionCards - _actionCards.length;
			
		}
		
		[Bindable(event="left_player_list_changed")]
		public function get leftPlayerCards():ArrayCollection
		{
			return _leftPlayerCards;
		}
		
		[Bindable(event="left_player_list_changed")]
		public function get leftPlayerCardsCount():int
		{
			return _leftPlayerCards.source.length;
		}
		
		[Bindable(event="formation_changed")]
		public function get goalkeeper():GoalkeeperCard
		{
			return getPlayerCardByPosition(Position.GOALKEEPER) as GoalkeeperCard;
		}
		
		[Bindable(event="formation_changed")]
		public function get hasGoalkeeper():Boolean
		{
			return hasPlayerCardByPosition(Position.GOALKEEPER);
		}
		
		[Bindable(event="formation_changed")]
		public function get defender01():PlayerCard
		{
			return getPlayerCardByPosition(Position.DEFENDER_01);
		}
		
		[Bindable(event="formation_changed")]
		public function get hasDefender01():Boolean
		{
			return hasPlayerCardByPosition(Position.DEFENDER_01);
		}
		
		[Bindable(event="formation_changed")]
		public function get defender02():PlayerCard
		{
			return getPlayerCardByPosition(Position.DEFENDER_02);
		}
		
		[Bindable(event="formation_changed")]
		public function get hasDefender02():Boolean
		{
			return hasPlayerCardByPosition(Position.DEFENDER_02);
		}
		
		[Bindable(event="formation_changed")]
		public function get midfield():PlayerCard
		{
			return getPlayerCardByPosition(Position.MIDFIELD);
		}
		
		[Bindable(event="formation_changed")]
		public function get hasMidfield():Boolean
		{
			return hasPlayerCardByPosition(Position.MIDFIELD);
		}
		
		[Bindable(event="formation_changed")]
		public function get forward01():PlayerCard
		{
			return getPlayerCardByPosition(Position.FORWARD_01);
		}
		
		[Bindable(event="formation_changed")]
		public function get hasForward01():Boolean
		{
			return hasPlayerCardByPosition(Position.FORWARD_01);
		}
		
		[Bindable(event="formation_changed")]
		public function get forward02():PlayerCard
		{
			return getPlayerCardByPosition(Position.FORWARD_02);
		}
		
		[Bindable(event="formation_changed")]
		public function get hasForward02():Boolean
		{
			return hasPlayerCardByPosition(Position.FORWARD_02);
		}
		
		[Bindable(event="formation_changed")]
		public function get substitutionDefence():PlayerCard
		{
			return getPlayerCardByPosition(Position.SUB_DEFENCE);
		}
		
		[Bindable(event="formation_changed")]
		public function get hasSubstitutionDefence():Boolean
		{
			return hasPlayerCardByPosition(Position.SUB_DEFENCE);
		}
		
		[Bindable(event="formation_changed")]
		public function get substitutionMidfield():PlayerCard
		{
			return getPlayerCardByPosition(Position.SUB_MIDFIELD);
		}
		
		[Bindable(event="formation_changed")]
		public function get hasSubstitutionMidfield():Boolean
		{
			return hasPlayerCardByPosition(Position.SUB_MIDFIELD);
		}
		
		[Bindable(event="formation_changed")]
		public function get substitutionForward():PlayerCard
		{
			return getPlayerCardByPosition(Position.SUB_FORWARD);
		}
		
		[Bindable(event="formation_changed")]
		public function get hasSubstitutionForward():Boolean
		{
			return hasPlayerCardByPosition(Position.SUB_FORWARD);
		}
		
		[Bindable(event="formation_changed")]
		public function get playersCount():int
		{
			var count:int = 0;
			if (hasPlayerCardByPosition(Position.GOALKEEPER)) 	count++;
			if (hasPlayerCardByPosition(Position.DEFENDER_01)) 	count++;
			if (hasPlayerCardByPosition(Position.DEFENDER_02))	count++;
			if (hasPlayerCardByPosition(Position.MIDFIELD)) 	count++;
			if (hasPlayerCardByPosition(Position.FORWARD_01))	count++;
			if (hasPlayerCardByPosition(Position.FORWARD_02))	count++;
			if (hasPlayerCardByPosition(Position.SUB_DEFENCE))	count++;
			if (hasPlayerCardByPosition(Position.SUB_MIDFIELD))	count++;
			if (hasPlayerCardByPosition(Position.SUB_FORWARD))	count++;
			return count;
		}
		
		public function get isValid():Boolean
		{
			return	_name != "" &&
					isFormationValid && 
					isActionListValid;
		}
		
		public function get isActivable():Boolean
		{
			return	true;
					/*
					_name != "" &&
					isFormationValid && 
					isActionListValid;
					*/
		}
		
		public function get isFormationValid():Boolean
		{
			return	hasPlayerCardByPosition(Position.GOALKEEPER) &&
					hasPlayerCardByPosition(Position.DEFENDER_01) && 
					hasPlayerCardByPosition(Position.DEFENDER_02) && 
					hasPlayerCardByPosition(Position.MIDFIELD) && 
					hasPlayerCardByPosition(Position.FORWARD_01) && 
					hasPlayerCardByPosition(Position.FORWARD_02);
		}
		
		public function get isActionListValid():Boolean
		{
			// TODO: rimuovere il controllo di retrocompatibilità
			// return	actionCardsCount == Deck.REQUIRED_ACTION_CARDS ||
			// 			actionCardsCount == Deck.REQUIRED_ACTIONS_OLD ;
			
			return requiredActionCards == actionCardsCount;
			
		}

		public function get isLeftActionCardsFiltered():Boolean
		{
			return _leftActionCards.filterFunction != null;
		}
		
		public function get isLeftPlayerCardsFiltered():Boolean
		{
			return _leftPlayerCards.filterFunction != null;
		}
		
		/**
		 * True if the Deck has a valid unique id so synch with the server 
		 * @return True if the Deck is saved on the server
		 */
		public function get isSynchronized():Boolean
		{
			return _id != DataObject.INVALID_ID;
		}
		
		public function get requiredActionCards():int
		{
			if (Application.instance.isInit && Application.instance.isUserLogged)
			{
				return Application.instance.user.deckSize;
			}
			else
			{
				return FlexDeck.REQUIRED_ACTION_CARDS;
			}
		}
		
		
		// -------------------------------------------------------------------------------
		// PRIVATE METHODS
		// -------------------------------------------------------------------------------
		
		protected function getActionCardIndex(instanceId:uint):int
		{
			for (var i:int = 0; i<_actionCards.source.length; i++)
			{
				// var actionCard:ActionCard = _actionCards.getItemAt(i) as ActionCard;
				var actionCard:ActionCard = _actionCards.source[i] as ActionCard;
				if (actionCard.instanceId == instanceId) return i;
			}
			return -1;
		}
		
		protected function getActionCardInstanceCount(card:ActionCard):int
		{
			var count:int = 0;
			// Count how many card instances are already present into the deck
			for (var i:int = 0; i<_actionCards.source.length; i++)
			{
				// var c:Card = _actionCards.getItemAt(i) as Card;
				var c:ActionCard = _actionCards.source[i] as ActionCard;
				// If the card has the same id, increment the count
				if (c.cardId == card.cardId) count++;
			}
			return count;
		}
		
		protected function addLeftActionCard(card:ActionCard):void
		{
			_leftActionCards.addItem(card);
			// Dispatch event
			dispatchLeftActionListChanged();
		}
		
		protected function removeLeftActionCard(card:ActionCard):void
		{
			// Count how many card instances are already present into the deck
			for (var i:int = 0; i<_leftActionCards.source.length; i++)
			{
				var c:ActionCard = _leftActionCards.getItemAt(i) as ActionCard;
				// If the card has the same id, increment the count
				if (c.instanceId == card.instanceId)
				{
					_leftActionCards.removeItemAt(i);
					break;
				}
			}
			// Dispatch event
			dispatchLeftActionListChanged();
		}
		
		protected function addLeftPlayerCard(card:PlayerCard):void
		{
			_leftPlayerCards.addItem(card);
			// Dispatch event
			dispatchLeftPlayerListChanged();
			_leftPlayerCards.refresh();
		}
		
		protected function removeLeftPlayerCard(card:PlayerCard):void
		{
			// Count how many card instances are already present into the deck
			for (var i:int = 0; i<_leftPlayerCards.source.length; i++)
			{
				var c:PlayerCard = _leftPlayerCards.getItemAt(i) as PlayerCard;
				// If the card has the same id, increment the count
				if (c.instanceId == card.instanceId)
				{
					_leftPlayerCards.removeItemAt(i);
					break;
				}
			}
			// Dispatch event
			dispatchLeftPlayerListChanged();
			_leftPlayerCards.refresh();
		}
		
		protected function setPlayerCard(position:Position, card:PlayerCard):void
		{
			if (!_collection.containsCard(card))
				throw new Error("PlayerCard card is not contained by deck card collection!");
			// Apply new player
			_formation[position] = card;
		}
			
		protected function updateLeftActionCards():void
		{
			// Disable event dispatch
			dispatchEnabled = false;
			// Disables filters
			var filtered:Boolean = isLeftActionCardsFiltered; 
			if (filtered) _leftActionCards.filterFunction = null;
			// Temporary left card list
			var leftActions:ArrayList = new ArrayList();
			// Loop over collection action cards
			for (var i:int = 0; i< _collection.actionCardsCount; i++)
			{
				var actionCard:ActionCard = _collection.getActionCard(i);
				if (!containsActionCard(actionCard))
				{
					leftActions.addItem(actionCard);
				}
			}
			// Refill left action card list
			_leftActionCards.removeAll();
			_leftActionCards.addAll(leftActions);
			// Enables filters
			if (filtered)  _leftActionCards.filterFunction = leftActionCardsFilter;
			// Enable event dispatch
			dispatchEnabled = true;
			// Dispatch events
			dispatchLeftActionListChanged();
			
		}
		
		protected function updateLeftPlayersCards():void
		{
			// Disable event dispatch
			dispatchEnabled = false;
			// Disables filters
			var filtered:Boolean = isLeftPlayerCardsFiltered;
			if (filtered) _leftPlayerCards.filterFunction = null;
			// Temporary left player list
			var leftPlayers:ArrayList = new ArrayList();
			// Loop over collection action cards
			for (var i:int = 0; i< _collection.playerCardsCount; i++)
			{
				var playerCard:PlayerCard = _collection.getPlayerCard(i);
				if (!containsPlayerCard(playerCard))
				{
					leftPlayers.addItem(playerCard);
				}
			}
			// Refill left action card list
			_leftPlayerCards.removeAll();
			_leftPlayerCards.addAll(leftPlayers);
			// Enables filters
			if (filtered)  _leftPlayerCards.filterFunction = leftPlayerCardsFilter;
			// Enable event dispatch
			dispatchEnabled = true;
			// Dispatch events
			dispatchLeftPlayerListChanged();
		}
		
		protected function leftActionCardsFilter(item:Object):Boolean
		{
			var filter:Boolean = true;
			try
			{
				filter = _leftActionCardsFilters.evaluate(item);
			}
			catch (e:Error)
			{
				// TODO_LOG	
				ErrorWindow.show("Deck > leftActionCardsFilter() unexpected error", e.message);
			}
			return filter;
		}
		
		protected function leftPlayerCardsFilter(item:Object):Boolean
		{
			var filter:Boolean = true;
			try
			{
				filter = _leftPlayerCardsFilters.evaluate(item);
			}
			catch (e:Error)
			{
				// TODO_LOG	
				ErrorWindow.show("Deck > leftPlayerCardsFilter() unexpected error", e.message);
			}
			return filter;
		}
		
		// DATA OBJECT IMPLEMENTATION
		
		override protected function validate(data:Object):void
		{
			if (data == null)															throwLoadPropNullError();
			if (!data.hasOwnProperty(JSON_PROPERTY_DECK)) 								throwLoadMissingPropError("deck");
			if (!data[JSON_PROPERTY_DECK].hasOwnProperty(JSON_PROPERTY_FORMATION)) 		throwLoadMissingPropError("deck formation");
			if (!data[JSON_PROPERTY_DECK].hasOwnProperty(JSON_PROPERTY_ACTION_CARDS)) 	throwLoadMissingPropError("deck action cards");
		}
		
		override protected function fill(data:Object):void
		{
			// Disable event dispatch
			dispatchEnabled = false;
			// Raw
			var raw:Object = data[JSON_PROPERTY_DECK];
			var raw_formation:Object = raw[JSON_PROPERTY_FORMATION];
			var raw_action_cards:Array = raw[JSON_PROPERTY_ACTION_CARDS];
			// Retrieve player cards
			var gk_id:int 	= int(raw_formation[Position.GOALKEEPER.sign]);
			var d1_id:int 	= int(raw_formation[Position.DEFENDER_01.sign]);
			var d2_id:int 	= int(raw_formation[Position.DEFENDER_02.sign]);
			var mf_id:int 	= int(raw_formation[Position.MIDFIELD.sign]);
			var f1_id:int 	= int(raw_formation[Position.FORWARD_01.sign]);
			var f2_id:int 	= int(raw_formation[Position.FORWARD_02.sign]);
			var sub_df:int	= int(raw_formation[Position.SUB_DEFENCE.sign]);
			var sub_mf:int	= int(raw_formation[Position.SUB_MIDFIELD.sign]);
			var sub_fk:int	= int(raw_formation[Position.SUB_FORWARD.sign]);
			// Action cards check
			for (var i:int=0; i<raw_action_cards.length; i++)
			{
				if (!_collection.containsCardByInstanceId(int(raw_action_cards[i])))
					throw new Error("Action card '" + raw_action_cards[i] + "' doesn't exists into collection");
			}
			// Apply formation
			_formation[Position.GOALKEEPER]  	= _collection.containsCardByInstanceId(gk_id)	? _collection.getCardByInstanceId(gk_id)	: null;
			_formation[Position.DEFENDER_01] 	= _collection.containsCardByInstanceId(d1_id)	? _collection.getCardByInstanceId(d1_id)	: null;
			_formation[Position.DEFENDER_02] 	= _collection.containsCardByInstanceId(d2_id)	? _collection.getCardByInstanceId(d2_id)	: null;
			_formation[Position.MIDFIELD] 	 	= _collection.containsCardByInstanceId(mf_id)	? _collection.getCardByInstanceId(mf_id)	: null;
			_formation[Position.FORWARD_01]	 	= _collection.containsCardByInstanceId(f1_id)	? _collection.getCardByInstanceId(f1_id)	: null;
			_formation[Position.FORWARD_02]	 	= _collection.containsCardByInstanceId(f2_id)	? _collection.getCardByInstanceId(f2_id)	: null;
			_formation[Position.SUB_DEFENCE]	= _collection.containsCardByInstanceId(sub_df)	? _collection.getCardByInstanceId(sub_df)	: null; 
			_formation[Position.SUB_MIDFIELD]	= _collection.containsCardByInstanceId(sub_mf)	? _collection.getCardByInstanceId(sub_mf)	: null;
			_formation[Position.SUB_FORWARD]	= _collection.containsCardByInstanceId(sub_fk)	? _collection.getCardByInstanceId(sub_fk)	: null;
			// Apply action cards
			_actionCards.removeAll();
			for (var j:int=0; j<raw_action_cards.length; j++)
			{
				_actionCards.addItem( _collection.getCardByInstanceId( raw_action_cards[j]) );
			}
			// Update the left card lists
			updateLeftCards();
			// Enable event dispatch
			dispatchEnabled = true;
			// Dispatch events
			dispatchFormationChanged();
			dispatchActionListChanged();
		}
		
		// EVENT DISPATCHERS
		
		protected function dispatchFormationChanged():void
		{
			dispatchEvent(new Event(EVENT_FORMATION_CHANGED));
		}
		
		protected function dispatchActionListChanged():void
		{
			dispatchEvent(new Event(EVENT_ACTION_LIST_CHANGED));
		}
		
		protected function dispatchLeftActionListChanged():void
		{
			dispatchEvent(new Event(EVENT_LEFT_ACTION_LIST_CHANGED));
			_leftActionCards.refresh();
			
		}
		
		protected function dispatchLeftPlayerListChanged():void
		{
			dispatchEvent(new Event(EVENT_LEFT_PLAYER_LIST_CHANGED));
			_leftPlayerCards.refresh();
		}

		// -------------------------------------------------------------------------------
		// PUBLIC METHODS
		// -------------------------------------------------------------------------------
		
		public function getPlayerCardByPosition(position:Position):PlayerCard
		{
			return _formation[position] as PlayerCard;
		}
		
		public function getLeftPlayerCard(index:int):PlayerCard
		{
			return _leftPlayerCards.source[index] as PlayerCard;
		}
		
		public function setPlayerCardByPosition(position:Position, card:PlayerCard):void
		{
			// Save the current card
			var removedCard:PlayerCard = _formation[position];
			// Apply the change
			setPlayerCard(position, card);
			// Setup the left card list
			removeLeftPlayerCard(card);
			if (removedCard) addLeftPlayerCard(removedCard);
			// Dispatch formation changed event
			dispatchFormationChanged();
		}
		
		public function resetPlayerCardByPosition(position:Position):void
		{
			// Save the current card
			var removedCard:PlayerCard = _formation[position];
			// Resets player
			_formation[position] = null;
			// Setup the left card list
			addLeftPlayerCard(removedCard);
			// dispatch formation changed event
			dispatchFormationChanged();
		}
		
		public function hasPlayerCardByPosition(position:Position):Boolean
		{
			return _formation[position] != null;
		}
		
		public function containsPlayerCard(playerCard:PlayerCard):Boolean
		{
			return containsPlayerCardByInstanceId(playerCard.instanceId);
		}
		
		public function containsPlayerCardByInstanceId(instanceId:uint):Boolean
		{
			// Loop over formation player instance id
			for each (var value:Object in _formation)
			{
				var playerCard:PlayerCard = value as PlayerCard;
				if (value != null && instanceId == playerCard.instanceId) return true;
			}
			return false;
		}
		
		public function swapPlayersByPosition(source:Position, destination:Position):void
		{
			// Retrieve the players
			var src:PlayerCard = _formation[source] as PlayerCard;
			var dst:PlayerCard = _formation[destination] as PlayerCard;
			// Retrieve roles by position
			var src_role:Role = Role.getByPosition(source);
			var dst_role:Role = Role.getByPosition(destination);
			// Check role
			if (src_role != dst_role) throw new Error("Players cards with different Role canno be swapped!");
			// Swap
			_formation[source] 		= dst;
			_formation[destination] = src;
			// Dispatch events to refresh
			dispatchFormationChanged();
		}
		
		public function getActionCard(index:int):ActionCard
		{
			return _actionCards.source[index] as ActionCard;
		}
		
		public function getActionCardByInstanceId(instanceId:uint):ActionCard
		{
			var card:ActionCard;
			var index:int = getActionCardIndex(instanceId);
			if (index > -1) card = _actionCards.source[index];
			return card;
		}		
		
		public function getActionCardsInstanceId():Array
		{
			var actionCardsId:Array = [];
			for (var i:int=0; i<_actionCards.source.length; i++)
			{
				var card:ActionCard = getActionCard(i);
				actionCardsId.push(card.instanceId);
			}
			return actionCardsId; 
		}
		
		public function getLeftActionCard(index:int):ActionCard
		{
			return _leftActionCards.source[index] as ActionCard;
		}
		
		public function containsActionCard(card:ActionCard):Boolean
		{
			return containsActionCardByInstanceId(card.instanceId);
		}
		
		public function containsActionCardByInstanceId(instanceId:uint):Boolean
		{
			return getActionCardIndex(instanceId) != -1;
		}
		
		public function addActionCard(card:ActionCard):void
		{
			// Card check
			if (containsActionCard(card)) throw new Error("Action card instance already present into deck!");
			if (!_collection.containsCard(card as Card)) throw new Error("Action card is not contained by deck card collection!");
			if (getActionCardInstanceCount(card) >= card.limit) throw new Error("Max number of Card already present into deck!");
			// Add the card
			_actionCards.addItem(card);
			// remove the card from the left cards
			removeLeftActionCard(card);
			// Dispatch events
			dispatchActionListChanged();
		}
		
		public function removeActionCard(card:ActionCard):void
		{
			// Card check
			var index:int = getActionCardIndex(card.instanceId);
			if (index == -1) throw new Error("Action card instance is NOT present into deck!");
			// Remove the card
			_actionCards.removeItemAt(index);
			// add the card to the left actions list
			addLeftActionCard(card);
			// Dispatch events
			dispatchActionListChanged();
		}
		
		public function setLeftActionCardsFilterByRole(role:Role):void
		{
			// Adds or update the filter
			_leftActionCardsFilters.add(new DataFilter("role", "role", role));
			// apply the filter function if needed
			if (!isLeftActionCardsFiltered)
			{
				_leftActionCards.filterFunction = leftActionCardsFilter;
			}
			_leftActionCards.refresh();
		}
		
		public function setLeftActionCardsFilterBySchool(school:School):void
		{
			// Adds or update the filter
			// _leftActionCardsFilters.add(new DataFilter("school", "school", school));
			_leftActionCardsFilters.add(new SchoolsFilter("school", school));
			// apply the filter function if needed
			if (!isLeftActionCardsFiltered)
			{
				_leftActionCards.filterFunction = leftActionCardsFilter;
			}
			_leftActionCards.refresh();
		}
		
		public function setLeftActionCardsFilterByName(name:String):void
		{
			// Adds or update the filter
			_leftActionCardsFilters.add(new StringFilter("name", "name", name));
			// apply the filter function if needed
			if (!isLeftActionCardsFiltered)
			{
				_leftActionCards.filterFunction = leftActionCardsFilter;
			}
			_leftActionCards.refresh();
		}
		
		public function resetLeftActionCardsFilter(filterName:String=null):void
		{
			// Removes filter data
			if (filterName != null)
			{
				_leftActionCardsFilters.removeByName(filterName);
			}
			else
			{
				_leftActionCardsFilters.clear();
			}
			// If there are no more filters, removes filter function
			if (!_leftActionCardsFilters.hasFilters) _leftActionCards.filterFunction = null;
			_leftActionCards.refresh();
		}
		
		public function setLeftPlayerCardsFilterByRole(role:Role):void
		{
			// Adds or update the filter
			_leftPlayerCardsFilters.add(new DataFilter("role", "role", role));
			// apply the filter function if needed
			if (!isLeftPlayerCardsFiltered)
			{
				_leftPlayerCards.filterFunction = leftPlayerCardsFilter;
			}
			_leftPlayerCards.refresh();
		}
		
		public function setLeftPlayerCardsFilterBySchool(school:School):void
		{
			// Adds or update the filter
			// _leftPlayerCardsFilters.add(new DataFilter("school", "school", school));
			_leftPlayerCardsFilters.add(new SchoolsFilter("school", school));
			// apply the filter function if needed
			if (!isLeftPlayerCardsFiltered)
			{
				_leftPlayerCards.filterFunction = leftPlayerCardsFilter;
			}
			_leftPlayerCards.refresh();
		}
		
		public function setLeftPlayerCardsFilterByName(name:String):void
		{
			// Adds or update the filter
			_leftPlayerCardsFilters.add(new StringFilter("name", "name", name));
			// apply the filter function if needed
			if (!isLeftPlayerCardsFiltered)
			{
				_leftPlayerCards.filterFunction = leftPlayerCardsFilter;
			}
			_leftPlayerCards.refresh();
		}
		
		public function resetLeftPlayerCardsFilter(filterName:String=null):void
		{
			// Removes filter data
			if (filterName != null)
			{
				_leftPlayerCardsFilters.removeByName(filterName);
			}
			else
			{
				_leftPlayerCardsFilters.clear();
			}
			// If there are no more filters, removes filter function
			if (!_leftPlayerCardsFilters.hasFilters) _leftPlayerCards.filterFunction = null;
			_leftPlayerCards.refresh();
		}
		
		public function updateLeftCards():void
		{
			updateLeftPlayersCards();
			updateLeftActionCards();
		}
		
		public function equals(deck:FlexDeck):Boolean
		{
			// Basic checks
			if (deck == null) return false;
			if (deck == this) return true;
			// Check action cards
			return	_id		== deck.id &&				
					_name == deck.name &&
					equalsFormation(deck) &&
					equalsActionCards(deck);
		}
		
		public function equalsActionCards(deck:FlexDeck):Boolean
		{
			if (_actionCards.length != deck.actionCardsCount) return false;
			for (var i:int=0; i < _actionCards.length; i++)
			{
				var c1:Card = getActionCard(i);
				var c2:Card = deck.getActionCard(i);
				if (!c1.equals(c2)) return false;
			}
			return true;
		}
		
		public function equalsFormation(deck:FlexDeck):Boolean
		{
			if (playersCount != deck.playersCount) return false;
			return	( (!hasGoalkeeper 			&& !deck.hasGoalkeeper) 			|| _formation[Position.GOALKEEPER].equals(deck.goalkeeper)				) &&
					( (!hasDefender01 			&& !deck.hasDefender01) 			|| _formation[Position.DEFENDER_01].equals(deck.defender01) 			) &&
					( (!hasDefender02 			&& !deck.hasDefender02) 			|| _formation[Position.DEFENDER_02].equals(deck.defender02) 			) &&
					( (!hasMidfield				&& !deck.hasMidfield) 				|| _formation[Position.MIDFIELD].equals(deck.midfield) 					) &&
					( (!hasForward01			&& !deck.hasForward01) 				|| _formation[Position.FORWARD_01].equals(deck.forward01) 				) &&
					( (!hasForward02			&& !deck.hasForward02) 				|| _formation[Position.FORWARD_02].equals(deck.forward02) 				) &&
					( (!hasSubstitutionDefence	&& !deck.hasSubstitutionDefence)	|| _formation[Position.SUB_DEFENCE].equals(deck.substitutionDefence) 	) &&
					( (!hasSubstitutionMidfield	&& !deck.hasSubstitutionMidfield)	|| _formation[Position.SUB_MIDFIELD].equals(deck.substitutionMidfield) 	) &&
					( (!hasSubstitutionForward	&& !deck.hasSubstitutionForward)	|| _formation[Position.SUB_FORWARD].equals(deck.substitutionForward) 	);
		}
		
		public function copyActionCards(deck:FlexDeck):void
		{
			// Disable event dispatch
			dispatchEnabled = false;
			// Temporary array
			var actionCards:ArrayList = new ArrayList();
			var leftActionCards:ArrayList = new ArrayList();
			// Check if the collection is the same
			if (deck.collection == _collection)
			{
				// Copy all the action cards references
				for (var i:int = 0; i<deck.actionCardsCount; i++)
				{
					actionCards.addItem( deck.getActionCard(i) );
				}
				// Clone all the left action cards
				for (var j:int = 0; j<deck.leftActionCardsCount; j++)
				{
					leftActionCards.addItem( deck.getLeftActionCard(j) );
				}
				// Copy action cards
				_actionCards.removeAll();
				_actionCards.addAll(actionCards);
				// Copy left action cards
				_leftActionCards.removeAll();
				_leftActionCards.addAll(leftActionCards);
			}
			else
			{
				// Copy the action cards
				for (var x:int = 0; x < deck.actionCardsCount; x++)
				{
					var card:ActionCard = deck.getActionCard(x);
					// Card check
					if (!_collection.containsCard(card)) throw new Error("Deck copy failed, action card '" + card.instanceId + "'  doesn't exists in current collection");
					// Adds the card to the temporary list
					actionCards.addItem( _collection.getCardByInstanceId(card.instanceId) );
				}
				// Clears the action cards
				_actionCards.removeAll();
				// Copy action cards
				for (var y:int = 0; y < actionCards.length; y++)
				{
					addActionCard( actionCards.getItemAt(y) as ActionCard);
				}
				// Update the left cards
				updateLeftCards();
			}
			// Enable event dispatch
			dispatchEnabled = true;
			// Dispatch change events
			dispatchActionListChanged();
			dispatchLeftActionListChanged();
		}
		
		public function copyFormation(deck:FlexDeck):void
		{
			// Collection check
			// if (deck.collection != _collection) throw new Error("Cannot copy formation from a deck in a different Collection");
			// Disable event dispatch
			dispatchEnabled = false;
			// Check if the collection is the same
			if (deck.collection == _collection)
			{
				// Copy all formation references
				_formation[Position.GOALKEEPER]		= deck.hasGoalkeeper 			? deck.goalkeeper 				: null;
				_formation[Position.DEFENDER_01]	= deck.hasDefender01 			? deck.defender01				: null;
				_formation[Position.DEFENDER_02]	= deck.hasDefender02 			? deck.defender02				: null;
				_formation[Position.MIDFIELD]		= deck.hasMidfield				? deck.midfield					: null;
				_formation[Position.FORWARD_01]		= deck.hasForward02 			? deck.forward01				: null;
				_formation[Position.FORWARD_02]		= deck.hasForward02 			? deck.forward02				: null;
				_formation[Position.SUB_DEFENCE]	= deck.hasSubstitutionDefence	? deck.substitutionDefence		: null;
				_formation[Position.SUB_MIDFIELD]	= deck.hasSubstitutionMidfield	? deck.substitutionMidfield		: null;
				_formation[Position.SUB_FORWARD]	= deck.hasSubstitutionForward	? deck.substitutionForward		: null;
				// Temporary array
				var leftPlayerCards:ArrayList = new ArrayList();
				// Clone all the left player cards
				for (var k:int = 0; k<deck.leftPlayerCardsCount; k++)
				{
					leftPlayerCards.addItem(deck.getLeftPlayerCard(k));
				}
				// Copy left player cards
				_leftPlayerCards.removeAll();
				_leftPlayerCards.addAll(leftPlayerCards);
			}
			else
			{
				// Check fromation cards
				if (deck.hasGoalkeeper 				&& !_collection.containsCard(deck.goalkeeper)) 			throw new Error("Deck copy failed, deck goalkeeper card doesn't exists in current collection");
				if (deck.hasDefender01 				&& !_collection.containsCard(deck.defender01)) 			throw new Error("Deck copy failed, deck defender 01 card  doesn't exists in current collection");
				if (deck.hasDefender02 				&& !_collection.containsCard(deck.defender02)) 			throw new Error("Deck copy failed, deck defender 02 card doesn't exists in current collection");
				if (deck.hasMidfield 				&& !_collection.containsCard(deck.midfield)) 			throw new Error("Deck copy failed, deck midfield card doesn't exists in current collection");
				if (deck.hasForward01 				&& !_collection.containsCard(deck.forward01)) 			throw new Error("Deck copy failed, deck forward 01 card doesn't exists in current collection");
				if (deck.hasForward02 				&& !_collection.containsCard(deck.forward02)) 			throw new Error("Deck copy failed, deck forward 02 card doesn't exists in current collection");
				if (deck.hasSubstitutionDefence 	&& !_collection.containsCard(deck.substitutionDefence)) throw new Error("Deck copy failed, deck sub defence card doesn't exists in current collection");
				if (deck.hasSubstitutionMidfield	&& !_collection.containsCard(deck.substitutionMidfield))throw new Error("Deck copy failed, deck sub midfield card doesn't exists in current collection");
				if (deck.hasSubstitutionForward 	&& !_collection.containsCard(deck.substitutionForward)) throw new Error("Deck copy failed, deck sub forward card doesn't exists in current collection");
				// Copy all formation references
				_formation[Position.GOALKEEPER]		= deck.hasGoalkeeper 			? _collection.getCardByInstanceId(deck.goalkeeper.instanceId) 			as PlayerCard 	: null;
				_formation[Position.DEFENDER_01]	= deck.hasDefender01 			? _collection.getCardByInstanceId(deck.defender01.instanceId) 			as PlayerCard	: null;
				_formation[Position.DEFENDER_02]	= deck.hasDefender02 			? _collection.getCardByInstanceId(deck.defender02.instanceId) 			as PlayerCard	: null;
				_formation[Position.MIDFIELD]		= deck.hasMidfield				? _collection.getCardByInstanceId(deck.midfield.instanceId) 			as PlayerCard	: null;
				_formation[Position.FORWARD_01]		= deck.hasForward01 			? _collection.getCardByInstanceId(deck.forward01.instanceId) 			as PlayerCard	: null;
				_formation[Position.FORWARD_02]		= deck.hasForward02 			? _collection.getCardByInstanceId(deck.forward02.instanceId) 			as PlayerCard	: null;
				_formation[Position.SUB_DEFENCE]	= deck.hasSubstitutionDefence	? _collection.getCardByInstanceId(deck.substitutionDefence.instanceId) 	as PlayerCard	: null;
				_formation[Position.SUB_MIDFIELD]	= deck.hasSubstitutionMidfield	? _collection.getCardByInstanceId(deck.substitutionMidfield.instanceId) as PlayerCard	: null;
				_formation[Position.SUB_FORWARD]	= deck.hasSubstitutionForward	? _collection.getCardByInstanceId(deck.substitutionForward.instanceId) 	as PlayerCard	: null;
			}
			// Enable event dispatch
			dispatchEnabled = true;
			// Dispatch change events
			dispatchFormationChanged();
			dispatchLeftPlayerListChanged();
		}

		public function copy(deck:FlexDeck):void
		{
			// Disable event dispatch
			dispatchEnabled = false;
			// Temporary array
			var actionCards:ArrayList = new ArrayList();
			var leftActionCards:ArrayList = new ArrayList();
			var leftPlayerCards:ArrayList = new ArrayList();
			// Check if the collection is the same
			if (deck.collection == _collection)
			{
				// Copy all the action cards references
				for (var i:int = 0; i<deck.actionCardsCount; i++)
				{
					actionCards.addItem( deck.getActionCard(i) );
				}
				// Copy all the left action cards references
				for (var j:int = 0; j<deck.leftActionCardsCount; j++)
				{
					leftActionCards.addItem( deck.getLeftActionCard(j) );
				}
				// Copy all the left player cards references
				for (var k:int = 0; k<deck.leftPlayerCardsCount; k++)
				{
					leftPlayerCards.addItem( deck.getLeftPlayerCard(k) );
				}
				// Copy all formation references
				_formation[Position.GOALKEEPER]		= deck.hasGoalkeeper 			? deck.goalkeeper 			: null;
				_formation[Position.DEFENDER_01]	= deck.hasDefender01 			? deck.defender01			: null;
				_formation[Position.DEFENDER_02]	= deck.hasDefender02 			? deck.defender02			: null;
				_formation[Position.MIDFIELD]		= deck.hasMidfield				? deck.midfield				: null;
				_formation[Position.FORWARD_01]		= deck.hasForward02 			? deck.forward01			: null;
				_formation[Position.FORWARD_02]		= deck.hasForward02 			? deck.forward02			: null;
				_formation[Position.SUB_DEFENCE]	= deck.hasSubstitutionDefence	? deck.substitutionDefence	: null;
				_formation[Position.SUB_MIDFIELD]	= deck.hasSubstitutionMidfield	? deck.substitutionMidfield	: null;
				_formation[Position.SUB_FORWARD]	= deck.hasSubstitutionForward	? deck.substitutionForward	: null;
				// Copy action cards
				_actionCards.removeAll();
				_actionCards.addAll(actionCards);
				// Copy left action cards
				_leftActionCards.removeAll();
				_leftActionCards.addAll(leftActionCards);
				// Copy left player cards
				_leftPlayerCards.removeAll();
				_leftPlayerCards.addAll(leftPlayerCards);
			}
			else
			{
				// Check fromation cards
				if (deck.hasGoalkeeper 				&& !_collection.containsCard(deck.goalkeeper)) 			throw new Error("Deck copy failed, deck goalkeeper card doesn't exists in current collection");
				if (deck.hasDefender01 				&& !_collection.containsCard(deck.defender01)) 			throw new Error("Deck copy failed, deck defender 01 card  doesn't exists in current collection");
				if (deck.hasDefender02 				&& !_collection.containsCard(deck.defender02)) 			throw new Error("Deck copy failed, deck defender 02 card doesn't exists in current collection");
				if (deck.hasMidfield 				&& !_collection.containsCard(deck.midfield)) 			throw new Error("Deck copy failed, deck midfield card doesn't exists in current collection");
				if (deck.hasForward01 				&& !_collection.containsCard(deck.forward01)) 			throw new Error("Deck copy failed, deck forward 01 card doesn't exists in current collection");
				if (deck.hasForward02 				&& !_collection.containsCard(deck.forward02)) 			throw new Error("Deck copy failed, deck forward 02 card doesn't exists in current collection");
				if (deck.hasSubstitutionDefence 	&& !_collection.containsCard(deck.substitutionDefence)) throw new Error("Deck copy failed, deck sub defence card doesn't exists in current collection");
				if (deck.hasSubstitutionMidfield	&& !_collection.containsCard(deck.substitutionMidfield))throw new Error("Deck copy failed, deck sub midfield card doesn't exists in current collection");
				if (deck.hasSubstitutionForward 	&& !_collection.containsCard(deck.substitutionForward)) throw new Error("Deck copy failed, deck sub forward card doesn't exists in current collection");
				// Copy the action cards
				for (var x:int = 0; x < deck.actionCardsCount; x++)
				{
					var card:ActionCard = deck.getActionCard(x);
					// Card check
					if (!_collection.containsCard(card)) throw new Error("Deck copy failed, action card '" + card.instanceId + "'  doesn't exists in current collection");
					// Adds the card to the temporary list
					actionCards.addItem( _collection.getCardByInstanceId(card.instanceId) );
				}
				// Copy all formation references
				_formation[Position.GOALKEEPER]		= deck.hasGoalkeeper 			? _collection.getCardByInstanceId(deck.goalkeeper.instanceId) 			as PlayerCard 	: null;
				_formation[Position.DEFENDER_01]	= deck.hasDefender01 			? _collection.getCardByInstanceId(deck.defender01.instanceId) 			as PlayerCard	: null;
				_formation[Position.DEFENDER_02]	= deck.hasDefender02 			? _collection.getCardByInstanceId(deck.defender02.instanceId) 			as PlayerCard	: null;
				_formation[Position.MIDFIELD]		= deck.hasMidfield				? _collection.getCardByInstanceId(deck.midfield.instanceId) 			as PlayerCard	: null;
				_formation[Position.FORWARD_01]		= deck.hasForward01 			? _collection.getCardByInstanceId(deck.forward01.instanceId) 			as PlayerCard	: null;
				_formation[Position.FORWARD_02]		= deck.hasForward02 			? _collection.getCardByInstanceId(deck.forward02.instanceId) 			as PlayerCard	: null;
				_formation[Position.SUB_DEFENCE]	= deck.hasSubstitutionDefence	? _collection.getCardByInstanceId(deck.substitutionDefence.instanceId) 	as PlayerCard	: null;
				_formation[Position.SUB_MIDFIELD]	= deck.hasSubstitutionMidfield	? _collection.getCardByInstanceId(deck.substitutionMidfield.instanceId) as PlayerCard	: null;
				_formation[Position.SUB_FORWARD]	= deck.hasSubstitutionForward	? _collection.getCardByInstanceId(deck.substitutionForward.instanceId) 	as PlayerCard	: null;
				// Clears the action cards
				_actionCards.removeAll();
				// Copy action cards
				for (var y:int = 0; y < actionCards.length; y++)
				{
					addActionCard( actionCards.getItemAt(y) as ActionCard);
				}
				// Update the left cards
				updateLeftCards();
			}
			// Copy properties
			_id		= deck.id;
			_name	= deck.name;
			// Copy loaded flag
			_loaded = deck.loaded;
			// Enable event dispatch
			dispatchEnabled = true;
			// Dispatch change events
			dispatchActionListChanged();
			dispatchFormationChanged();
			dispatchLeftActionListChanged();
			dispatchLeftPlayerListChanged();
		}
		
		public function clone():FlexDeck
		{
			var deck:FlexDeck = new FlexDeck(_id, _name, _collection);
			deck.copy(this);
			return deck;
		}
		
		public function duplicate(new_name:String):FlexDeck
		{
			// Create a new empty deck
			var new_deck:FlexDeck = _collection.createNewDeck(DataObject.INVALID_ID, new_name);
			// Disables event dispatch
			new_deck.dispatchEnabled = false;
			// Cop formation and action cards
			new_deck.copyFormation(this);
			new_deck.copyActionCards(this);
			// Ensables event dispatch
			new_deck.dispatchEnabled = true;
			return new_deck;
		}
		
		public function clearFormation(dispatch:Boolean=false):void
		{
			// Clears the formation!
			_formation[Position.GOALKEEPER] 	= null;
			_formation[Position.DEFENDER_01] 	= null;
			_formation[Position.DEFENDER_02] 	= null;
			_formation[Position.MIDFIELD] 		= null;
			_formation[Position.FORWARD_01] 	= null;
			_formation[Position.FORWARD_02]		= null;
			_formation[Position.FORWARD_02]		= null;
			_formation[Position.SUB_DEFENCE]	= null;
			_formation[Position.SUB_MIDFIELD]	= null;
			_formation[Position.SUB_FORWARD]	= null;
			// Check if dispatch events
			if (dispatch)
			{
				// Event dispatching
				dispatchFormationChanged();
			}
			// Update left player cards
			updateLeftPlayersCards();
		}
		
		public function clearActionCards(dispatch:Boolean=false):void
		{
			// Clears the action card list 
			_actionCards.removeAll();
			// Check if dispatch events
			if (dispatch)
			{
				// Event dispatching
				dispatchActionListChanged();
			}
			// Updates the left action cards
			updateLeftActionCards();
		}
				
		override public function clear():void
		{
			_id = DataObject.INVALID_ID;
			_name = FlexDeck.DEFAULT_NAME;
			// Clears the formation!
			_formation[Position.GOALKEEPER] 	= null;
			_formation[Position.DEFENDER_01] 	= null;
			_formation[Position.DEFENDER_02] 	= null;
			_formation[Position.MIDFIELD] 		= null;
			_formation[Position.FORWARD_01] 	= null;
			_formation[Position.FORWARD_02]		= null;
			_formation[Position.FORWARD_02]		= null;
			_formation[Position.SUB_DEFENCE]	= null;
			_formation[Position.SUB_MIDFIELD]	= null;
			_formation[Position.SUB_FORWARD]	= null;
			// Clears all the lists
			_actionCards.removeAll();
			_leftActionCards.removeAll();
			_leftPlayerCards.removeAll();
			// Filters
			_actionCardsFilter.clear();
			_leftActionCardsFilters.clear();
			_leftPlayerCardsFilters.clear();
			// Update left action card list
			updateLeftCards();
		}
		
		public function dump(source:Array = null, indentDepth:int = 0):Array
		{
			return Dumper.dump(this, null, source, indentDepth, ["collection"]);
		}
		
	}
}